package org.foreveross.jbpm.applicationImpl.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Named;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.SystemEventListenerFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.command.impl.KnowledgeCommandContext;
import org.drools.common.AbstractRuleBase;
import org.drools.compiler.BPMN2ProcessFactory;
import org.drools.compiler.PackageBuilder;
import org.drools.compiler.ProcessBuilderFactory;
import org.drools.impl.KnowledgeBaseImpl;
import org.drools.io.ResourceFactory;
import org.drools.io.impl.ByteArrayResource;
import org.drools.persistence.jpa.JPAKnowledgeService;
import org.drools.runtime.Environment;
import org.drools.runtime.EnvironmentName;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.xml.SemanticModules;
import org.foreveross.jbpm.application.vo.KoalaProcessInfoVO;
import org.foreveross.jbpm.core.KoalaProcessInfo;
import org.jbpm.bpmn2.handler.ServiceTaskHandler;
import org.jbpm.bpmn2.xml.BPMNSemanticModule;
import org.jbpm.compiler.xml.XmlProcessReader;
import org.jbpm.process.audit.JPAProcessInstanceDbLog;
import org.jbpm.process.audit.JPAWorkingMemoryDbLogger;
import org.jbpm.process.workitem.wsht.SyncWSHumanTaskHandler;
import org.jbpm.ruleflow.core.RuleFlowProcess;
import org.jbpm.task.TaskService;
import org.jbpm.task.service.TaskServiceSession;
import org.jbpm.task.service.local.LocalTaskService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.xml.sax.SAXException;

import javax.annotation.*;

@Named("jbpmSupport")
public class JbpmSupport {

	@Inject
	private EntityManagerFactory entityManagerFactory;

	@Inject
	private JpaTransactionManager transactionManager;

	private StatefulKnowledgeSession ksession;

	private KnowledgeBuilder kbuilder;

	private TaskServiceSession taskServiceSession;

	private org.jbpm.task.service.TaskService tService;

	private TaskService server;

	private KoalaWSHumanTaskHandler humanTaskHandler;

	private static Map<String, Integer> activeProcessMap = new HashMap<String, Integer>();

	private KnowledgeBase kbase;

	/**
	 * <dt>核心就是两个session：
	 * <dd>KnowledgeSession 可以完全通过spring drools配置成功
	 * <dd>TaskServiceSession 这里通过手动编码产生local service
	 * 
	 * <dt>todo 学习点：
	 * <dd>能否完全使用spring xml配置出两个session
	 * <dd>usergroup callback的作用和用法进一步研究
	 */
	@PostConstruct
	public void init() {

		// 为 ksession 设置log
		// Properties props = new Properties();
		// props.setProperty("drools.dialect.java.compiler", "JANINO");
		// KnowledgeBuilderConfiguration config =
		// KnowledgeBuilderFactory.newKnowledgeBuilderConfiguration(props,
		// null);
		// KnowledgeBuilder kbuilder =
		// KnowledgeBuilderFactory.newKnowledgeBuilder(config);

		// KnowledgeBuilder kbuilder =
		// KnowledgeBuilderFactory.newKnowledgeBuilder(conf)

		// kbuilder.
		// kbuilder.add(new ClassPathResource("Sample.bpmn"),
		// ResourceType.BPMN2);
		// KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
		// kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

		// ksession = kbase.newStatefulKnowledgeSession();
		// PackageBuilderConfiguration conf = new PackageBuilderConfiguration();
		// conf.setCompiler( PackageBuilderConfiguration.JANINO );
		// PackageBuilder builder = new PackageBuilder( conf );

		try {
			kbase = readKnowledgeBase();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		Environment env = KnowledgeBaseFactory.newEnvironment();
		env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);
		env.set(EnvironmentName.TRANSACTION_MANAGER, transactionManager);
		ksession = JPAKnowledgeService.newStatefulKnowledgeSession(kbase, null,
				env);

		new JPAWorkingMemoryDbLogger(ksession);
		new JPAProcessInstanceDbLog(ksession.getEnvironment());

		// 创建 local human service 及其 handler
		org.jbpm.task.service.TaskService tService = new org.jbpm.task.service.TaskService(
				entityManagerFactory,
				SystemEventListenerFactory.getSystemEventListener());
		taskServiceSession = tService.createSession();
		server = new LocalTaskService(taskServiceSession);

		// TODO 事务该如何设置？
		// taskServiceSession.setTransactionType("RESOURCE_LOCAL");
		humanTaskHandler = new KoalaWSHumanTaskHandler(server, ksession);
		// humanTaskHandler.setConnection("localhost", 8080);
		humanTaskHandler.setLocal(true);
		humanTaskHandler.connect();
		ksession.getWorkItemManager().registerWorkItemHandler("Human Task",
				humanTaskHandler);
		ksession.getWorkItemManager().registerWorkItemHandler("Service Task",
				new ServiceTaskHandler());
		ksession.addEventListener(new JBPMProcessEventListener());
		// set user group callback
		System.setProperty("jbpm.usergroup.callback",
				"org.jbpm.task.service.DefaultUserGroupCallbackImpl");

		initActiveProcess();
	}

	public TaskService getTaskService() {
		return server;
	}

	public KoalaWSHumanTaskHandler getHumanTaskHandler() {
		return humanTaskHandler;
	}

	public EntityManagerFactory getEntityManagerFactory() {
		return entityManagerFactory;
	}

	public void setEntityManagerFactory(
			EntityManagerFactory entityManagerFactory) {
		this.entityManagerFactory = entityManagerFactory;
	}

	public StatefulKnowledgeSession getKsession() {
		return ksession;
	}

	public void setKsession(StatefulKnowledgeSession ksession) {
		this.ksession = ksession;
	}

	public TaskServiceSession getTaskServiceSession() {
		return taskServiceSession;
	}

	public void setTaskServiceSession(TaskServiceSession taskServiceSession) {
		this.taskServiceSession = taskServiceSession;
	}

	private List<KoalaProcessInfoVO> getDBResource() {
		final Query processQuery = entityManagerFactory.createEntityManager()
				.createQuery("select w from KoalaProcessInfo w");
		List<KoalaProcessInfo> processes = processQuery.getResultList();
		List<KoalaProcessInfoVO> vos = new ArrayList<KoalaProcessInfoVO>();
		for (KoalaProcessInfo process : processes) {
			KoalaProcessInfoVO info = new KoalaProcessInfoVO();
			BeanUtils.copyProperties(process, info);
			vos.add(info);
		}
		return vos;
	}

	public void initActiveProcess() {
		String sql = "select max(info.processName),max(info.versionNum) from KoalaProcessInfo info group by info.processName";
		List<Object[]> list = (List<Object[]>) entityManagerFactory
				.createEntityManager().createQuery(sql).getResultList();
		for (Object[] obj : list) {
			activeProcessMap.put((String) obj[0], (Integer) obj[1]);
		}
	}

	public String getActiveProcess(String process) {
		if (activeProcessMap.containsKey(process)) {
			return process + "@" + activeProcessMap.get(process);
		} else {
			return process;
		}
	}

	public void addProcessToCenter(KoalaProcessInfo processInfo) throws Exception {
		addKoalaProcessInfo(processInfo);
		if (activeProcessMap.containsKey(processInfo.getProcessName())) {
			int version = activeProcessMap.get(processInfo.getProcessName());
			if (version <= processInfo.getVersionNum()) {
				activeProcessMap.put(processInfo.getProcessName(),
						processInfo.getVersionNum());
			}
		} else {
			activeProcessMap.put(processInfo.getProcessName(),
					processInfo.getVersionNum());
		}
	}

	private KnowledgeBase readKnowledgeBase() throws Exception {
		kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		List<KoalaProcessInfoVO> resources = getDBResource();
		// 从数据库中加载并解析
		for (KoalaProcessInfoVO info : resources) {
			addKoalaProcessInfoVO(info);
		}
		return kbuilder.newKnowledgeBase();
	}

	private void addKoalaProcessInfoVO(KoalaProcessInfoVO info)
			throws Exception {
		try {
			addProcess(info.getData());
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}
	
	private void addProcess(byte[] data) throws Exception{
		
		kbuilder.add(new ByteArrayResource(data),
				ResourceType.BPMN2);
		KnowledgeBuilderErrors errors = kbuilder.getErrors();
		
		if(errors.size()>0){
			throw new Exception(errors.toString());
		}
		
	}

	private void addKoalaProcessInfo(KoalaProcessInfo info) throws Exception {
		//检验发布的BMPN2的正确性
		PackageBuilder packageBuilder = new PackageBuilder();
		BPMNSemanticModule d = new BPMNSemanticModule();
		packageBuilder.getPackageBuilderConfiguration().addSemanticModule(d);
		packageBuilder.addProcessFromXml(new ByteArrayResource(info.getData()));
		if(packageBuilder.getErrors().size()>0){
			throw new Exception(packageBuilder.getErrors().toString());
		}
		
		try {
			addProcess(info.getData());
		} catch (Exception e) {
			e.printStackTrace();
			throw e;
		}
	}

}